﻿#pragma warning(disable:4005)

#include "fan_maker.h"
#include "model_list.h"

#include <QDebug>
#include <QTimer>
#include <QTime>
#include <QEventLoop>
#include <QCoreApplication>

#include <AIS_Shape.hxx>
#include <AIS_ColoredShape.hxx>
#include <AIS_PointCloud.hxx>
#include <Prs3d_PointAspect.hxx>
#include <BRepBuilderAPI_MakePolygon.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <BRepBuilderAPI_MakeEdge2d.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <BRepPrimAPI_MakeRevol.hxx>
#include <BRepPrimAPI_MakeCylinder.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <BRepPrimAPI_MakeCone.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepAlgoAPI_Fuse.hxx>
#include <BRepFilletAPI_MakeFillet.hxx>
#include <BRepLib.hxx>
#include <GC_MakeEllipse.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Solid.hxx>
#include <gp_Elips.hxx>
#include <gp_Elips2d.hxx>
#include <gp_Circ2d.hxx>
#include <Font_BRepTextBuilder.hxx>
#include <Font_BRepFont.hxx>
#include <Geom_BezierCurve.hxx>
#include <Geom_OffsetCurve.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <Geom_BezierSurface.hxx>
#include <Geom_Ellipse.hxx>
#include <Geom_BSplineSurface.hxx>
#include <GeomAPI_PointsToBSplineSurface.hxx>
#include <GeomFill.hxx>
#include <Geom2d_BezierCurve.hxx>
#include <Geom2d_OffsetCurve.hxx>
#include <Geom2d_Ellipse.hxx>
#include <BRep_CurveOnSurface.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <GCPnts_AbscissaPoint.hxx>

#include <iostream>
#include <thread>
#include <random>

struct Params
{
    const double baseRadius = 230;

    const double ballHeight = 917.5;
    const double motorRadius = 32; // 电机
    const double motorHeight = 60;

    const double fanAxisOffset = 35;
    const double fanAxisHeight = 1100;
    const double fanHubRadius = motorRadius * 1.2;
    const double fanHubHeight = 30;
    const double fanShroudInnerRadius = 148; // 前罩
    const double fanShroudThick = 2;

    const double backShroudLength = 55; // 后罩

    const double motorFanDist = 30;

    const int buttonColorR = 255;
    const int buttonColorG = 202;
    const int buttonColorB = 0;
    const double buttonTravel = 10;

    const double frontCoverDist = 175;
    const double frontCoverHeight = 10;
    const double frontCorverCenterRadius = 43;

    const int branchCnt = 67;

    ModelList* ms;
};

FanMaker::FanMaker(QObject* parent, ModelList* ms)
    : QObject(parent), _d(std::make_shared<Params>()), _prevButton(-1), _fanRPM(60)
{
    _d->ms = ms;
}

FanMaker::~FanMaker()
{
}
#define PARALLEL 0
void FanMaker::Display(Handle(AIS_InteractiveContext) context)
{
    _context = context;

#if PARALLEL
    std::vector<std::thread> threads;
    threads.emplace_back(std::thread(std::bind(&FanMaker::MakeBase, this)));
    threads.emplace_back(std::thread(std::bind(&FanMaker::MakeVerticalColumn, this)));
    threads.emplace_back(std::thread(std::bind(&FanMaker::MakeButtons, this)));
    //threads.emplace_back(std::thread(std::bind(&FanMaker::MakeShell, this)));
    //threads.emplace_back(std::thread(std::bind(&FanMaker::MakeFan, this)));
    //threads.emplace_back(std::thread(std::bind(&FanMaker::MakeFrontCover, this)));
    //threads.emplace_back(std::thread(std::bind(&FanMaker::MakeBackCover, this)));
    //threads.emplace_back(std::thread(std::bind(&FanMaker::MakeText, this)));

    for (std::thread& t : threads)
    {
        if (t.joinable())
        {
            t.join();
        }
    }

#else

    MakeBase();

    MakeVerticalColumn();

    MakeButtons();

    MakeShell();

    MakeFan();

    MakeFrontCover();

    MakeBackCover();;

    MakeText();

#endif

    _context->Activate(AIS_Shape::SelectionMode(TopAbs_VERTEX));
    context->UpdateCurrentViewer();
}

void FanMaker::OnLeftButtonClicked(double x, double y, double z)
{
    std::cout << x << " " << y << " " << z << std::endl;

    if (!_context)
    {
        return;
    }
    _context->InitDetected();
    if (_context->HasDetected())
    {
        _context->InitDetected();
        Handle(AIS_InteractiveObject) ais = _context->DetectedInteractive();
        if (!ais.IsNull())
        {
            auto it = std::find(_buttons.begin(), _buttons.end(), ais);
            if (it != _buttons.end())
            {
                OnButtonClicked(std::distance(_buttons.begin(), it));
            }
        }
    }
}

void FanMaker::OnKeyPressed(int key) // 鼠标交互存在问题，改用按钮
{
    if (key < 0x30 || key > 0x33)
    {
        return;
    }

    int button = key - 0x30;
    OnButtonClicked(size_t(button));
}

void FanMaker::MakeBase() const
{
    // 下部
    BRepBuilderAPI_MakePolygon mk_polygon;
    mk_polygon.Add(gp_Pnt());
    mk_polygon.Add(gp_Pnt(200, 0, 0));
    mk_polygon.Add(gp_Pnt(_d->baseRadius, 0, 18));
    mk_polygon.Add(gp_Pnt(_d->baseRadius, 0, 40));
    mk_polygon.Add(gp_Pnt(0, 0, 40));
    mk_polygon.Close();

    TopoDS_Wire wire = mk_polygon.Wire();
    TopoDS_Face face = BRepBuilderAPI_MakeFace(wire);

    BRepPrimAPI_MakeRevol mk_revol(face, gp::OZ());
    TopoDS_Shape bottom = mk_revol.Shape();

    // 上部
    BRepBuilderAPI_MakePolygon mk_polygon_upper;
    mk_polygon_upper.Add(gp_Pnt(0, 0, 40));
    mk_polygon_upper.Add(gp_Pnt(_d->baseRadius, 0, 40));
    mk_polygon_upper.Add(gp_Pnt(220, 0, 55));
    mk_polygon_upper.Add(gp_Pnt(0, 0, 55));
    mk_polygon_upper.Close();

    TopoDS_Wire wire_upper = mk_polygon_upper.Wire();
    TopoDS_Face face_upper = BRepBuilderAPI_MakeFace(wire_upper);

    BRepPrimAPI_MakeRevol mk_revol_upper(face_upper, gp::OZ());
    TopoDS_Shape upper = mk_revol_upper.Shape();

    bottom = Fillet(bottom, 5);
    DisplayShape(bottom, "基座-下部", Color(12, 12, 12));
    upper = Fillet(upper, 5);
    DisplayShape(upper, "基座-上部", Quantity_NOC_GRAY);
}

void FanMaker::MakeVerticalColumn() const
{
    // 立柱
    TopoDS_Shape cylinder = MakeCylinder(gp_Ax2(gp_Pnt(0, 0, 55), gp::DZ()), 55.0 / 2.0, 650);
    DisplayShape(cylinder, "立柱-下方圆柱");

    // 衔接锥
    TopoDS_Shape cone = BRepPrimAPI_MakeCone(gp_Ax2(gp_Pnt(0, 0, 705), gp::DZ()),
        55.0 / 2.0, 102 / 2, 55);
    DisplayShape(cone, "立柱-衔接锥");

    // 按钮圆柱
    TopoDS_Shape button_cylinder = MakeCylinder(gp_Ax2(gp_Pnt(0, 0, 755), gp::DZ()), 120 / 2.0, 190);
    //button_cylinder = Fillet(button_cylinder, 5);
    DisplayShape(button_cylinder, "立柱-按钮圆柱");

    BRepBuilderAPI_MakePolygon mk_polygon(
        gp_Pnt(-100, -75, 890),
        gp_Pnt(-100, 75, 947),
        gp_Pnt(-100, -75, 947));
    mk_polygon.Close();
    TopoDS_Wire tri_wire = mk_polygon.Wire();
    TopoDS_Face tri_face = BRepBuilderAPI_MakeFace(tri_wire);
    TopoDS_Shape trim_tri = BRepPrimAPI_MakePrism(tri_face, gp_Vec(200, 0, 0));
    DisplayShape(trim_tri, "立柱-裁剪圆锥");

    // 衔接球
    TopoDS_Shape sphere = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(0, 0, _d->ballHeight), gp::DZ()),
        115.0 / 2);
    DisplayShape(sphere, "立柱-衔接球");

    // 按钮槽
    const double prism_y_loc = -55;
    gp_Ax2 ellipse_loc(gp_Pnt(0, prism_y_loc, 866), -gp::DY(), gp::DX());
    Handle(Geom_Curve) ellipse = GC_MakeEllipse(ellipse_loc, 11.2, 6.2).Value();
    TopoDS_Edge e_edge = BRepBuilderAPI_MakeEdge(ellipse);
    TopoDS_Shape trim_button3 = Extrude({ e_edge }, gp_Vec(0, -100, 0));
    DisplayShape(trim_button3, "立柱-按钮槽");

    // 阵列按钮槽
    gp_Trsf trsf;
    trsf.SetTranslation(gp_Vec(0, 0, -25));
    TopoDS_Shape trim_button2 = BRepBuilderAPI_Transform(trim_button3, trsf);
    TopoDS_Shape trim_button1 = BRepBuilderAPI_Transform(trim_button2, trsf);
    TopoDS_Shape trim_button0 = BRepBuilderAPI_Transform(trim_button1, trsf);

    TopoDS_Shape fuse_shape = Fuse({ cylinder, cone, button_cylinder });
    DisplayShape(fuse_shape, "立柱-按钮圆柱");

    TopoDS_Shape result = Cut(fuse_shape, { trim_tri, sphere,
        trim_button3, trim_button2, trim_button1, trim_button0 });
    DisplayShape(result, "立柱-布尔运算后");

    result = Fillet(result, 2, 2);
    DisplayShape(result, "立柱-结果-倒圆角后", Quantity_NOC_GRAY);
}

void FanMaker::MakeButtons() const
{
    // 按钮
    const double prism_y_loc = -55;
    gp_Ax2 ellipse_loc(gp_Pnt(0, prism_y_loc, 866), -gp::DY(), gp::DX());
    Handle(Geom_Curve) ellipse = GC_MakeEllipse(ellipse_loc, 11, 6).Value();
    TopoDS_Edge e_edge = BRepBuilderAPI_MakeEdge(ellipse);
    double prism_y = -_d->buttonTravel - 2;
    TopoDS_Shape button3 = Extrude({ e_edge }, gp_Vec(0, prism_y, 0));
    DisplayShape(button3, "按钮-③");

    // 阵列按钮槽
    gp_Trsf trsf;
    trsf.SetTranslation(gp_Vec(0, 0, -25));
    TopoDS_Shape button2 = BRepBuilderAPI_Transform(button3, trsf);
    TopoDS_Shape button1 = BRepBuilderAPI_Transform(button2, trsf);
    TopoDS_Shape button0 = BRepBuilderAPI_Transform(button1, trsf);
    DisplayShape(button2, "按钮-②");
    DisplayShape(button1, "按钮-①");
    DisplayShape(button0, "按钮-〇");

    // 文字
    gp_Vec prism_vec(0, 1, 0);
    gp_Ax2 loc(gp_Pnt(0, prism_y_loc + prism_y, 866), -gp::DY(), gp::DX());
    TopoDS_Shape trim_text3 = PrismText("3", loc, prism_vec, 12);
    TopoDS_Shape trim_text2 = PrismText("2", loc = loc.Transformed(trsf), prism_vec, 12);
    TopoDS_Shape trim_text1 = PrismText("1", loc = loc.Transformed(trsf), prism_vec, 12);
    TopoDS_Shape trim_text0 = PrismText("0", loc = loc.Transformed(trsf), prism_vec, 12);
    DisplayShape(trim_text3, "按钮-文字-3");
    DisplayShape(trim_text2, "按钮-文字-2");
    DisplayShape(trim_text1, "按钮-文字-1");
    DisplayShape(trim_text0, "按钮-文字-0");

    button3 = Cut(button3, trim_text3);
    button2 = Cut(button2, trim_text2);
    button1 = Cut(button1, trim_text1);
    button0 = Cut(button0, trim_text0);

    _buttons.resize(4);
    Quantity_Color button_color = Color(_d->buttonColorR, _d->buttonColorG, _d->buttonColorB);
    _buttons[0] = DisplayShape(button0, "按钮-结果-0", button_color);
    _buttons[1] = DisplayShape(button1, "按钮-结果-1", button_color);
    _buttons[2] = DisplayShape(button2, "按钮-结果-2", button_color);
    _buttons[3] = DisplayShape(button3, "按钮-结果-3", button_color);
}

void FanMaker::MakeShell() const
{
    // 衔接球
    TopoDS_Shape sphere = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(0, 0, _d->ballHeight), gp::DZ()),
        115.0 / 2);
    DisplayShape(sphere, "外壳-衔接球");

    // 立柱
    gp_Ax2 box_loc(gp_Pnt(0, 0, 960), gp::DZ(), gp::DX());
    TopoDS_Shape column = MakeCylinder(box_loc, _d->motorRadius * 0.6, 120, -20);
    DisplayShape(column, "外壳-内部电机立柱");

    // 电机
    gp_Ax2 fan_revol_loc = FanRevolLocBase();
    TopoDS_Shape motor = MakeCylinder(fan_revol_loc, _d->motorRadius, 60);
    DisplayShape(motor, "外壳-内部电机");

    // 前后拼接处
    const double inner_radius = _d->fanShroudInnerRadius;
    const double outer_radius = inner_radius + _d->fanShroudThick;
    TopoDS_Shape outer_cylinder = MakeCylinder(fan_revol_loc, outer_radius, 2);
    TopoDS_Shape trim_cylinder = MakeCylinder(fan_revol_loc, inner_radius, _d->baseRadius, -_d->baseRadius);
    TopoDS_Shape connect_ring = Cut(outer_cylinder, trim_cylinder);
    DisplayShape(connect_ring, "外壳-前后罩连接环", Quantity_NOC_GRAY);

    // 风扇罩子
    TopoDS_Shape front_revol_shell = MakeShellFrontRevol(fan_revol_loc.Axis());
    DisplayShape(front_revol_shell, "外壳-前端旋转外壳");

    // 前端外壳
    TopoDS_Shape front_shell = Fuse({ Cut(sphere, trim_cylinder),
        front_revol_shell, column, motor });
    DisplayShape(front_shell, "外壳-结果-前端外壳");

    auto ais = DisplayShape(front_shell, "前端-外壳", Quantity_NOC_GRAY);
    ais->SetTransparency();
}

gp_Ax2 FanMaker::FanRevolLocBase() const
{
    return gp_Ax2(gp_Pnt(0, _d->fanAxisOffset, _d->fanAxisHeight), -gp::DY(), gp::DX());
}

TopoDS_Shape FanMaker::MakeShellFrontRevol(const gp_Ax1& revol_axis) const
{
    // 样条外形
    TColgp_Array1OfPnt2d poles(1, 4);
    const double max_z = _d->fanShroudInnerRadius + _d->fanShroudThick + revol_axis.Location().Z() - 2;
    poles.SetValue(1, gp_Pnt2d(_d->fanAxisOffset - 2, max_z));
    poles.SetValue(2, gp_Pnt2d(-34, max_z));
    poles.SetValue(3, gp_Pnt2d(-118, max_z - 2));
    poles.SetValue(4, gp_Pnt2d(-140, max_z - 15));
    Handle(Geom2d_BezierCurve) outer_curve = new Geom2d_BezierCurve(poles);

    // 偏置内壁
    const double offset = _d->fanShroudThick;
    Handle(Geom2d_OffsetCurve) inner_curve = new Geom2d_OffsetCurve(outer_curve, offset);
    TopoDS_Edge edge0 = BRepBuilderAPI_MakeEdge2d(outer_curve).Edge();
    TopoDS_Edge edge2 = BRepBuilderAPI_MakeEdge2d(inner_curve).Edge();
    edge2.Reverse();

    // 前端圆弧连接
    gp_Pnt2d outer_end = outer_curve->Value(outer_curve->LastParameter());
    gp_Pnt2d inner_end = inner_curve->Value(inner_curve->LastParameter());
    gp_Pnt2d center = (outer_end.XY() + inner_end.XY()) * 0.5;
    gp_Circ2d connect_circle(gp_Ax2d(center, gp_Vec2d(outer_end, inner_end)), offset * 0.5);
    TopoDS_Edge edge1 = BRepBuilderAPI_MakeEdge2d(connect_circle, 0, M_PI).Edge();

    // 后端直线连接
    gp_Pnt2d inner_bg = inner_curve->Value(inner_curve->FirstParameter());
    gp_Pnt2d outer_bg = outer_curve->Value(outer_curve->FirstParameter());
    TopoDS_Edge edge3 = BRepBuilderAPI_MakeEdge2d(inner_bg, outer_bg);

    TopoDS_Wire wire = BRepBuilderAPI_MakeWire(edge0, edge1, edge2, edge3);
    BRepLib::BuildCurves3d(wire);
    TopoDS_Face face = BRepBuilderAPI_MakeFace(wire);

    // 变换至YOZ
    gp_Trsf trsf;
    trsf.SetTransformation(gp::YOZ(), gp::XOY());
    TopoDS_Shape yoz_face = BRepBuilderAPI_Transform(face, trsf);
    //DisplayShape(yoz_face, Color(232, 236, 212));
    //return yoz_face;

    TopoDS_Shape front_shell = BRepPrimAPI_MakeRevol(yoz_face, revol_axis);

    return front_shell;
}

void FanMaker::MakeFan() const
{
    TopoDS_Shape fan_hub = MakeFanHub();
    DisplayShape(fan_hub, "风扇轮毂", Color(42, 73, 114));

    TopoDS_Shape fan_blade0 = MakeBlade();

    // 叶片阵列
    gp_Ax2 fan_revol_loc = FanRevolLocBase();
    gp_Trsf trsf;
    const int blade_num = 5;
    trsf.SetRotation(fan_revol_loc.Axis(), M_PI * 2 / blade_num);
    TopoDS_Shape fan_blade1 = BRepBuilderAPI_Transform(fan_blade0, trsf);
    TopoDS_Shape fan_blade2 = BRepBuilderAPI_Transform(fan_blade1, trsf);
    TopoDS_Shape fan_blade3 = BRepBuilderAPI_Transform(fan_blade2, trsf);
    TopoDS_Shape fan_blade4 = BRepBuilderAPI_Transform(fan_blade3, trsf);

    _fan = DisplayBlades({ fan_blade0,fan_blade1,fan_blade2,fan_blade3, fan_blade4 });

    //TopoDS_Shape blades = Compound({ fan_blade0,fan_blade1,fan_blade2,fan_blade3,
    //    fan_blade4 });
    //_fan = DisplayShape(blades, Color(42, 73, 114));
}

TopoDS_Shape FanMaker::MakeFanHub() const
{
    // 电机轴
    gp_Ax2 fan_revol_loc = FanRevolLocBase();
    TopoDS_Shape motor_fan_connector = MakeCylinder(fan_revol_loc, _d->motorRadius * 0.5,
        _d->motorHeight + _d->motorFanDist, _d->motorHeight);

    // 风扇轴
    const double fan_hub_height = 30;
    TopoDS_Shape fan_hub = MakeCylinder(fan_revol_loc, _d->fanHubRadius,
        _d->motorHeight + _d->motorFanDist + _d->fanHubHeight,
        _d->motorHeight + _d->motorFanDist);

    return Fuse({ motor_fan_connector, fan_hub });
}

double Rad(double degree_angle)
{
    return degree_angle / 180.0 * M_PI;
}

TopoDS_Shape FanMaker::MakeRuledBlade() const
{
    gp_Ax2 revol_surf_loc = FanRevolLocBase();

    const double hub_ymax = revol_surf_loc.Location().Y() - _d->motorHeight - _d->motorFanDist;
    const double hub_ymin = hub_ymax - _d->fanHubHeight;
    const double inner_radius = _d->fanHubRadius;
    const double outer_radius = _d->fanShroudInnerRadius - 1; // 留一个缝隙

    // 基准面
    const double y = revol_surf_loc.Location().Y() - _d->motorHeight -
        _d->motorFanDist - _d->fanHubHeight * 0.5;
    const double z0 = revol_surf_loc.Location().Z() + _d->motorRadius * 0.8;
    gp_Ax2 loc0(gp_Pnt(0, y, z0), gp::DZ(), gp_Vec(1, 2, 0));
    Handle(Geom_Ellipse) ec0 = new Geom_Ellipse(loc0, _d->fanHubHeight * 0.45, 5);

    const double z3 = z0 + _d->fanShroudInnerRadius * 0.7;
    gp_Ax2 loc3(gp_Pnt(0, y, z3), gp::DZ(), gp_Vec(5, 1, 0));
    Handle(Geom_Ellipse) ec3 = new Geom_Ellipse(loc3, _d->fanHubHeight * 2, 1);

    Handle(Geom_CylindricalSurface) surf0 = new Geom_CylindricalSurface(revol_surf_loc, inner_radius);
    const double v1 = _d->motorHeight + _d->motorFanDist;
    const double v2 = _d->motorHeight + _d->motorFanDist + _d->fanHubHeight;
    gp_Pnt2d center(M_PI, (v1 + v2) * 0.5);
    const double range = std::min(v2 - v1, M_PI * 2);
    Handle(Geom2d_Ellipse) ellipse0 = new Geom2d_Ellipse(gp_Ax22d(center, gp_Vec2d(1, 0)), range * 0.1,
        range * 0.01);
    TopoDS_Edge edge0 = BRepBuilderAPI_MakeEdge(ellipse0, surf0);
    BRepLib::BuildCurve3d(edge0);
    double cf = 0, cl = 0;
    Handle(Geom_Curve) curve0 = BRep_Tool::Curve(edge0, cf, cl);

    Handle(Geom_CylindricalSurface) surf1 = new Geom_CylindricalSurface(revol_surf_loc,
        inner_radius + (outer_radius - inner_radius) * 0.33);
    Handle(Geom_CylindricalSurface) surf2 = new Geom_CylindricalSurface(revol_surf_loc,
        inner_radius + (outer_radius - inner_radius) * 0.67);
    Handle(Geom_CylindricalSurface) surf3 = new Geom_CylindricalSurface(revol_surf_loc,
        outer_radius);

    Handle(Geom2d_Ellipse) ellipse3 = new Geom2d_Ellipse(gp_Ax22d(center, gp_Vec2d(1, 1)), range * 0.12,
        range * 0.005);

    TopoDS_Edge edge3 = BRepBuilderAPI_MakeEdge(ellipse3, surf3);
    BRepLib::BuildCurve3d(edge3);

    Handle(Geom_Curve) curve3 = BRep_Tool::Curve(edge3, cf, cl);

    Handle(Geom_Surface) ruled_surf = GeomFill::Surface(curve0, curve3);
    TopoDS_Face ruled_face = BRepBuilderAPI_MakeFace(ruled_surf, Precision::Confusion());
    return ruled_face;

    TopoDS_Face face3 = BRepBuilderAPI_MakeFace(surf3, 0, 2 * M_PI, v1, v2, Precision::Confusion());
    return face3;
}


// 样条扫掠
TopoDS_Shape FanMaker::MakeBlade() const
{
    gp_Ax2 revol_surf_loc = FanRevolLocBase();

    const double hub_ymax = revol_surf_loc.Location().Y() - _d->motorHeight - _d->motorFanDist;
    const double hub_ymin = hub_ymax - _d->fanHubHeight;
    const double inner_radius = _d->fanHubRadius;
    const double outer_radius = _d->fanShroudInnerRadius - 10; // 留一个缝隙

    // 四个截面：0, 0.35, 0.68, 1
    // Z 轴高度
    std::vector<double> heights{
        inner_radius, inner_radius + (outer_radius - inner_radius) * 0.35,
        inner_radius + (outer_radius - inner_radius) * 0.68, outer_radius };
    // X 轴宽度
    std::vector<double> half_width{
        _d->fanHubRadius, _d->fanHubRadius * 1.3, _d->fanHubRadius * 1.6, _d->fanHubRadius * 1.9 };
    std::vector<double> angles_to_x{ Rad(20), Rad(40), Rad(60), Rad(80) };
    std::vector<double> thick{ 0.06, 0.05, 0.03, 0.01 };
    std::vector<Handle(Geom_Surface)> surfs(heights.size());
    std::vector<std::vector<gp_Pnt>> pnts(heights.size());
    for (size_t i = 0; i < heights.size(); i++)
    {
        surfs[i] = MakeBladeSectionSurf(heights[i], half_width[i], hub_ymin, hub_ymax);

        TopoDS_Face face = BRepBuilderAPI_MakeFace(surfs[i], Precision::Confusion());
        DisplayShape(face, "截面", Quantity_NOC_GREEN);

        std::vector<gp_Pnt2d> uvs = MakeBladeSectionUVs(angles_to_x[i], thick[i]);
        pnts[i] = GetPntsOnSurf(surfs[i], uvs);
        
        DisplayPnts(pnts[i], "构造采样点");
    }

    Handle(Geom_Surface) blade_surf = ApproxSurf(pnts);
    TopoDS_Face face = BRepBuilderAPI_MakeFace(blade_surf, Precision::Confusion());
    DisplayShape(face, "单个叶片", Quantity_NOC_GREEN);
    return face;
}

// 宽度按轮毂直径
Handle(Geom_Surface) FanMaker::MakeBladeSectionSurf(double height, double half_width,
    double hub_ymin, double hub_ymax) const
{
    gp_Ax2 fan_revol_loc = FanRevolLocBase();
    double z_base = fan_revol_loc.Location().Z();

    const double hubr = half_width;
    gp_Pnt p0(-hubr, hub_ymin, z_base + std::sqrt(height * height - hubr * hubr));
    gp_Pnt p1(0, hub_ymin, z_base + height);
    gp_Pnt p2(hubr, hub_ymin, z_base + std::sqrt(height * height - hubr * hubr));
    gp_Pnt p3(-hubr, hub_ymax, z_base + std::sqrt(height * height - hubr * hubr));
    gp_Pnt p4(0, hub_ymax, z_base + height);
    gp_Pnt p5(hubr, hub_ymax, z_base + std::sqrt(height * height - hubr * hubr));

    TColgp_Array2OfPnt pnts(1, 2, 1, 3);
    pnts.SetValue(2, 1, p0);
    pnts.SetValue(2, 2, p1);
    pnts.SetValue(2, 3, p2);
    pnts.SetValue(1, 1, p3);
    pnts.SetValue(1, 2, p4);
    pnts.SetValue(1, 3, p5);
    GeomAPI_PointsToBSplineSurface ptb(pnts);
    Handle(Geom_Surface) surf = ptb.Surface();
    surf->VReverse();
    return surf;
}

// angle: 与 X 轴夹角
std::vector<gp_Pnt2d> FanMaker::MakeBladeSectionUVs(double angle, double thick) const
{
    const double side_len = 0.4;// _d->fanHubHeight * 0.4;
    const double offset = 0.5; // 参数域 [0, 1]
    double u = angle < M_PI_2 * 0.5 ? -side_len : -side_len / std::tan(angle);
    double v = angle < M_PI_2 * 0.5 ? -side_len * std::tan(angle) : -side_len;
    gp_Pnt2d p0(u + offset, v + offset);

    u = -0.2 * side_len * std::sin(angle);
    v = 0.2 * side_len * std::cos(angle);
    gp_Pnt2d p1(u + offset, v + offset);

    angle *= 0.5;
    u = std::min(side_len, side_len / std::tan(angle));
    v = std::min(side_len, side_len * std::tan(angle));
    gp_Pnt2d p2(u + offset, v + offset);
    Handle(Geom2d_Curve) bezier = MakeBezier({ p0, p1, p1, p2 });
    Handle(Geom2d_OffsetCurve) bezier_offset = new Geom2d_OffsetCurve(bezier, -thick);

    double cf = bezier->FirstParameter();
    double cl = bezier->LastParameter();

    const size_t size = 50;
    std::vector<gp_Pnt2d> uvs(size * 2 + 2);
    for (size_t i = 0; i < size; i++)
    {
        const double param = cf + (cl - cf) * i / (size - 1);
        uvs[i] = bezier->Value(param);
    }

    gp_Pnt2d end0 = bezier->Value(bezier->LastParameter());
    gp_Pnt2d end1 = bezier_offset->Value(bezier_offset->LastParameter());
    gp_Dir2d nor = gp_Vec2d(end0, end1).GetNormal();
    gp_Pnt2d edge_uv = (end0.XY() + end1.XY()) * 0.5 + nor.XY() * thick;
    uvs[size] = edge_uv;

    for (size_t i = 0; i < size; i++)
    {
        const double param = cl - (cl - cf) * i / (size - 1);
        uvs[i + size + 1] = bezier_offset->Value(param);
    }

    end0 = bezier_offset->Value(bezier_offset->FirstParameter());
    end1 = bezier->Value(bezier->FirstParameter());
    nor = gp_Vec2d(end0, end1).GetNormal();
    edge_uv = (end0.XY() + end1.XY()) * 0.5 + nor.XY() * thick;
    uvs.back() = edge_uv;
    uvs.emplace_back(uvs.front()); // 首尾重合

    return uvs;
}

std::vector<gp_Pnt> FanMaker::GetPntsOnSurf(const Handle(Geom_Surface)& surf,
    const std::vector<gp_Pnt2d>& uvs) const
{
    std::vector<gp_Pnt> pnts(uvs.size());
    for (size_t i = 0; i < uvs.size(); i++)
    {
        pnts[i] = surf->Value(uvs[i].X(), uvs[i].Y());
    }
    return pnts;
}

Handle(Geom_Surface) FanMaker::ApproxSurf(const std::vector<std::vector<gp_Pnt>>& pnts) const
{
    if (pnts.empty() || pnts[0].empty())
    {
        return nullptr;
    }

    TColgp_Array2OfPnt surf_pnts(1, int(pnts.size()), 1, int(pnts[0].size()));
    for (size_t i = 0; i < pnts.size(); i++)
    {
        if (i > 0 && pnts[i - 1].size() != pnts[i].size())
        {
            return nullptr;
        }

        for (size_t j = 0; j < pnts[i].size(); j++)
        {
            surf_pnts.SetValue(int(i + 1), int(j + 1), pnts[i][j]);
        }

    }

    GeomAPI_PointsToBSplineSurface ptb(surf_pnts);
    return ptb.Surface();
}

void FanMaker::MakeFrontCover() const
{
    gp_Ax2 fan_base_loc = FanRevolLocBase();

    // 外形修剪
    const double rotate_z = fan_base_loc.Location().Z();
    const gp_Pnt tp0(0, -146, rotate_z);
    const gp_Pnt tp1(0, -146, 1129); // 极点
    const gp_Pnt tp2(0, -146, 1140);
    const gp_Pnt tp3(0, -144, 1225); // 极点
    const gp_Pnt tp4(0, -141, 1233);
    const gp_Pnt tp5(0, -153, 1233);
    const gp_Pnt tp6(0, -153, rotate_z);
    TopoDS_Edge te0 = BRepBuilderAPI_MakeEdge(MakeBezier({ tp0, tp1, tp1, tp2 }));
    TopoDS_Edge te1 = BRepBuilderAPI_MakeEdge(MakeBezier({ tp2, tp3, tp3, tp4 }));
    TopoDS_Edge te2 = BRepBuilderAPI_MakeEdge(tp4, tp5);
    TopoDS_Edge te3 = BRepBuilderAPI_MakeEdge(tp5, tp6);
    TopoDS_Edge te4 = BRepBuilderAPI_MakeEdge(tp6, tp0);
    TopoDS_Shape trim_shape = Revolve({ te0, te1, te2, te3, te4 }, fan_base_loc.Axis());
    DisplayShape(te0, "FrontCover-外形修剪-边0", Quantity_NOC_YELLOW);
    DisplayShape(te1, "FrontCover-外形修剪-边1", Quantity_NOC_YELLOW);
    DisplayShape(te2, "FrontCover-外形修剪-边2", Quantity_NOC_YELLOW);
    DisplayShape(te3, "FrontCover-外形修剪-边3", Quantity_NOC_YELLOW);
    DisplayShape(te4, "FrontCover-外形修剪-边4", Quantity_NOC_YELLOW);
    DisplayShape(trim_shape, "FrontCover-外形修剪体", Quantity_NOC_YELLOW);

    // 中心饼
    TopoDS_Shape logo_cylinder = MakeCylinder(fan_base_loc, _d->frontCorverCenterRadius,
        _d->frontCoverDist + _d->frontCoverHeight, _d->frontCoverDist);
    logo_cylinder = Cut(logo_cylinder, trim_shape);
    DisplayShape(logo_cylinder, "FrontCover-log cylinder", Quantity_NOC_YELLOW);

    // 文字
    gp_Ax2 text_loc = fan_base_loc;
    text_loc.SetLocation(gp_Pnt(0, -150, fan_base_loc.Location().Z()));
    TopoDS_Shape text_shape = PrismText("Mechanicoder", text_loc, gp_Vec(0, 92, 0));
    DisplayShape(text_shape, "FrontCover-文字", Quantity_NOC_YELLOW);
    logo_cylinder = Cut(logo_cylinder, text_shape);
    auto ais2 = DisplayShape(logo_cylinder, "FrontCover-布尔求减后", Color(42, 73, 114));

    // 网条外环
    const double inner_radius = 258 / 2.0;
    const double outer_radius = 266 / 2.0;
    TopoDS_Shape border_outer = MakeCylinder(fan_base_loc, outer_radius,
        _d->frontCoverDist + 2.1, _d->frontCoverDist);
    TopoDS_Shape border_inner = MakeCylinder(fan_base_loc, inner_radius,
        _d->frontCoverDist + 2.1, _d->frontCoverDist);
    TopoDS_Shape border_ring = Cut(border_outer, border_inner);
    border_ring = Fillet(border_ring, 0.2, 4);

    // 网条
    const double y = -148;
    const gp_Pnt bp0(-83, y, 1201);
    const gp_Pnt bp1(0, y, 1193);
    const gp_Pnt bp2(0, y, 1138);
    const gp_Pnt bp3(-2, y, 1138);
    const gp_Pnt bp4(0, y, 1190);
    const gp_Pnt bp5(-85, y, 1200);
    TopoDS_Edge be0 = BRepBuilderAPI_MakeEdge(MakeBezier({ bp0, bp1, bp1, bp2 }));
    TopoDS_Edge be2 = BRepBuilderAPI_MakeEdge(MakeBezier({ bp3, bp4, bp4, bp5 }));
    TopoDS_Edge be1 = BRepBuilderAPI_MakeEdge(bp2, bp3);
    TopoDS_Edge be3 = BRepBuilderAPI_MakeEdge(bp5, bp0);
    TopoDS_Shape branch = Extrude({ be0, be1, be2, be3 }, gp_Vec(0, 10, 0));
    DisplayShape(be0, "FrontCover-网条轮廓0", Quantity_NOC_YELLOW);
    DisplayShape(be2, "FrontCover-网条轮廓2", Quantity_NOC_YELLOW);
    DisplayShape(be1, "FrontCover-网条轮廓1", Quantity_NOC_YELLOW);
    DisplayShape(be3, "FrontCover-网条轮廓3", Quantity_NOC_YELLOW);
    DisplayShape(branch, "FrontCover-网条轮廓拉伸", Quantity_NOC_YELLOW);

    branch = Fillet(branch, 0.3);
    DisplayShape(branch, "FrontCover-网条倒圆角", Quantity_NOC_YELLOW);

    branch = Cut(branch, trim_shape);
    DisplayShape(branch, "FrontCover-网条裁剪后", Color(42, 73, 114));
    TopoDS_Shape all_branch = CircularPattern(branch, fan_base_loc.Axis(), _d->branchCnt);

    auto ais1 = DisplayShape(all_branch, "FrontCover-网条阵列", Color(42, 73, 114));
    auto ais3 = DisplayShape(border_ring, "前端-外环", Color(42, 73, 114));
    ais1->SetTransparency();
    ais2->SetTransparency();
    ais3->SetTransparency();
    return;

    // 网条阵列
    const size_t branch_cnt = _d->branchCnt;
    gp_Trsf trsf;
    trsf.SetRotation(fan_base_loc.Axis(), M_PI * 2.0 / double(branch_cnt));
    std::vector<TopoDS_Shape> branchs(branch_cnt);
    branchs[0] = branch;
    for (size_t i = 1; i < branch_cnt; i++)
    {
        branchs[i] = BRepBuilderAPI_Transform(branchs[i - 1], trsf);
    }

    // 合并
    branchs.insert(branchs.begin(), logo_cylinder);
    branchs.emplace_back(border_ring);

    TopoDS_Shape front_cover = Compound(branchs);
    //TopoDS_Shape front_cover = Cut(branch_shape, trim_shape);

    DisplayShape(front_cover, "前端网罩", Color(42, 73, 114));
}

void FanMaker::MakeBackCover() const
{
    const gp_Ax2 fan_revol_loc = FanRevolLocBase();
    double y = fan_revol_loc.Location().Y();

    // 前后拼接处
    const double inner_radius = _d->fanShroudInnerRadius;
    const double outer_radius = inner_radius + _d->fanShroudThick;
    const double shroud_length = _d->backShroudLength;
    TopoDS_Shape outer_cylinder = MakeCylinder(fan_revol_loc, outer_radius, 0, -shroud_length);
    TopoDS_Shape inner_cylinder = MakeCylinder(fan_revol_loc, inner_radius, 0, -shroud_length);
    TopoDS_Shape back_shroud = Cut(outer_cylinder, inner_cylinder);
    DisplayShape(outer_cylinder, "BackCover-外圆柱", Quantity_NOC_ALICEBLUE);
    DisplayShape(inner_cylinder, "BackCover-内圆柱", Quantity_NOC_ALICEBLUE);
    DisplayShape(back_shroud, "BackCover-外罩", Quantity_NOC_ALICEBLUE);

    // 后盖把手
    const double handle_radius = 16;
    const double handle_len = 40;
    TopoDS_Shape handle = MakeCylinder(fan_revol_loc, 26, -shroud_length, -shroud_length - handle_len);
    handle = Fillet(handle, 6, 2);
    DisplayShape(handle, "BackCover-网罩把手", Quantity_NOC_ALICEBLUE);

    // 网条
    y += shroud_length;
    gp_Pnt p0(0, y, 1118);
    gp_Pnt p1(-78, y, 1229);
    gp_Pnt p2(-77, y, 1230);
    gp_Pnt p3(2, y, 1118);
    Handle(Geom_BezierCurve) bc0 = MakeBezier({ p0, gp_Pnt(0, y, 1192), gp_Pnt(0, y, 1192), p1 });
    Handle(Geom_BezierCurve) bc2 = MakeBezier({ p2, gp_Pnt(1, y, 1193), gp_Pnt(1, y, 1193), p3 });
    TopoDS_Edge edge0 = BRepBuilderAPI_MakeEdge(bc0).Edge();
    TopoDS_Edge edge1 = BRepBuilderAPI_MakeEdge(p1, p2).Edge();
    TopoDS_Edge edge2 = BRepBuilderAPI_MakeEdge(bc2).Edge();
    TopoDS_Edge edge3 = BRepBuilderAPI_MakeEdge(p3, p0).Edge();
    TopoDS_Shape branch = Extrude({ edge0, edge1, edge2, edge3 }, gp_Vec(0, 25, 0));
    branch = Fillet(branch, 0.4, 2);
    DisplayShape(edge0, "BackCover-网条线0", Quantity_NOC_ALICEBLUE);
    DisplayShape(edge1, "BackCover-网条线1", Quantity_NOC_ALICEBLUE);
    DisplayShape(edge2, "BackCover-网条线2", Quantity_NOC_ALICEBLUE);
    DisplayShape(edge3, "BackCover-网条线3", Quantity_NOC_ALICEBLUE);
    DisplayShape(branch, "BackCover-网条", Quantity_NOC_ALICEBLUE);

    //DisplayShape(branch, Color(222, 0, 0));

    // 外部裁剪体
    gp_Pnt tp0(0, 115, 1126);
    gp_Pnt tp1(0, 112, 1229); // 极点
    gp_Pnt tp2(0, 98, 1247);
    gp_Pnt tp3(0, 92, 1251); // 极点
    gp_Pnt tp4(0, 35, 1250);
    gp_Pnt tp5(0, 35, 1258);
    gp_Pnt tp6(0, 155, 1258);
    gp_Pnt tp7(0, 130, 1126);
    TopoDS_Edge te0 = BRepBuilderAPI_MakeEdge(MakeBezier({ tp0, tp1, tp1, tp2 })).Edge();
    TopoDS_Edge te1 = BRepBuilderAPI_MakeEdge(MakeBezier({ tp2, tp3, tp3, tp4 })).Edge();
    TopoDS_Edge te2 = BRepBuilderAPI_MakeEdge(tp4, tp5);
    TopoDS_Edge te3 = BRepBuilderAPI_MakeEdge(tp5, tp6);
    TopoDS_Edge te4 = BRepBuilderAPI_MakeEdge(tp6, tp7);
    TopoDS_Edge te5 = BRepBuilderAPI_MakeEdge(tp7, tp0);
    TopoDS_Shape trim_outer = Revolve({ te0, te1, te2, te3, te4, te5 }, fan_revol_loc.Axis());
    DisplayShape(te0, "BackCover-弧形裁剪轮廓-0", Quantity_NOC_ALICEBLUE);
    DisplayShape(te1, "BackCover-弧形裁剪轮廓-1", Quantity_NOC_ALICEBLUE);
    DisplayShape(te2, "BackCover-弧形裁剪轮廓-2", Quantity_NOC_ALICEBLUE);
    DisplayShape(te3, "BackCover-弧形裁剪轮廓-3", Quantity_NOC_ALICEBLUE);
    DisplayShape(te4, "BackCover-弧形裁剪轮廓-4", Quantity_NOC_ALICEBLUE);
    DisplayShape(te5, "BackCover-弧形裁剪轮廓-5", Quantity_NOC_ALICEBLUE);
    DisplayShape(trim_outer, "BackCover-弧形裁剪体", Quantity_NOC_ALICEBLUE);

    // 内部裁剪体
    const double revol_z = fan_revol_loc.Location().Z();
    gp_Pnt tp8(0, 87, revol_z);
    gp_Pnt tp9(0, 87, 1243);
    gp_Pnt tp10(0, 91, 1243);
    gp_Pnt tp11(0, 105, 1199);  // 极点
    gp_Pnt tp12(0, 105, revol_z);
    TopoDS_Edge te6 = BRepBuilderAPI_MakeEdge(tp8, tp9).Edge();
    TopoDS_Edge te7 = BRepBuilderAPI_MakeEdge(tp9, tp10).Edge();
    TopoDS_Edge te8 = BRepBuilderAPI_MakeEdge(MakeBezier({ tp10, tp11, tp11, tp12 })).Edge();
    TopoDS_Edge te9 = BRepBuilderAPI_MakeEdge(tp12, tp8).Edge();
    TopoDS_Shape trim_inner = Revolve({ te6, te7, te8, te9 }, fan_revol_loc.Axis());

    branch = Cut(branch, { trim_outer, trim_inner });
    handle = Cut(handle, trim_inner);
    DisplayShape(branch, "BackCover-裁剪后网条", Quantity_NOC_ALICEBLUE);
    DisplayShape(handle, "BackCover-裁剪后把手", Quantity_NOC_ALICEBLUE);

    TopoDS_Shape all_branch = CircularPattern(branch, fan_revol_loc.Axis(), _d->branchCnt);
    DisplayShape(all_branch, "BackCover-把手阵列", Quantity_NOC_ALICEBLUE);

    //DisplayShape(trim_outer, Color(1, 0, 0));
    //DisplayShape(trim_inner, Color(1, 0, 0));
    auto ais1 = DisplayShape(back_shroud, "BackCover-结果-外壳", Color(42, 73, 114));
    auto ais2 = DisplayShape(all_branch, "BackCover-结果-网条", Color(42, 73, 114));
    auto ais3 = DisplayShape(handle, "BackCover-结果-把手", Color(42, 73, 114));
    ais1->SetTransparency();
    ais2->SetTransparency();
    ais3->SetTransparency();
}

void FanMaker::MakeText() const
{
    TopoDS_Shape text = PrismText("鼠标点击按钮，或\n按数字键(1 2 3 0)启动风扇",
        gp_Ax2(gp_Pnt(_d->baseRadius * 2, 0, 0.8 * _d->fanAxisHeight), -gp::DY(), gp::DX()),
        gp_Vec(1, -1, 1), 48);

    DisplayShape(text, "说明文本", Quantity_NOC_RED);
}

Handle(Geom_BezierCurve) FanMaker::MakeBezier(const std::vector<gp_Pnt>& poles) const
{
    if (poles.size() < 4)
    {
        return nullptr;
    }
    TColgp_Array1OfPnt b_poles(1, int(poles.size()));
    for (size_t i = 0; i < poles.size(); i++)
    {
        b_poles.SetValue(int(i + 1), poles[i]);
    }

    Handle(Geom_BezierCurve) bcurve = new Geom_BezierCurve(b_poles);
    return bcurve;
}

Handle(Geom2d_BezierCurve) FanMaker::MakeBezier(const std::vector<gp_Pnt2d>& poles) const
{
    if (poles.size() < 4)
    {
        return nullptr;
    }

    TColgp_Array1OfPnt2d b_poles(1, int(poles.size()));
    for (size_t i = 0; i < poles.size(); i++)
    {
        b_poles.SetValue(int(i + 1), poles[i]);
    }

    Handle(Geom2d_BezierCurve) bcurve = new Geom2d_BezierCurve(b_poles);
    return bcurve;
}

Quantity_Color FanMaker::Color(int r, int g, int b) const
{
    return Quantity_Color(r / 255.0, g / 255.0, b / 255.0, Quantity_TOC_sRGB);
}

TopoDS_Shape FanMaker::Fuse(const std::vector<TopoDS_Shape>& shapes) const
{
    if (shapes.empty())
    {
        return {};
    }
    else if (shapes.size() == 1)
    {
        return shapes[0];
    }
    else
    {
        TopoDS_Shape result = BRepAlgoAPI_Fuse(shapes[0], shapes[1]);
        for (size_t i = 2; i < shapes.size(); i++)
        {
            if (!shapes[i].IsNull())
            {
                result = BRepAlgoAPI_Fuse(result, shapes[i]);
            }
        }
        return result;
    }
}

TopoDS_Shape FanMaker::Cut(const TopoDS_Shape& s, const TopoDS_Shape& cutter) const
{
    return BRepAlgoAPI_Cut(s, cutter);
}

TopoDS_Shape FanMaker::Cut(const TopoDS_Shape& s,
    const std::vector<TopoDS_Shape>& cutters) const
{
    BRepAlgoAPI_Cut cut;
    TopTools_ListOfShape args, tools;
    args.Append(s);
    for (const auto& cutter : cutters)
    {
        tools.Append(cutter);
    }

    cut.SetArguments(args);
    cut.SetTools(tools);
    cut.Build();

    if (!cut.IsDone())
    {
        return TopoDS_Shape();
    }

    return cut.Shape();
}

TopoDS_Shape FanMaker::Compound(const std::vector<TopoDS_Shape>& shapes) const
{
    TopoDS_Compound comp;
    BRep_Builder builder;
    builder.MakeCompound(comp);
    for (const auto& s : shapes)
    {
        if (!s.IsNull())
        {
            builder.Add(comp, s);
        }
    }
    return comp;
}

TopoDS_Shape FanMaker::PrismText(const NCollection_String& text,
    const gp_Ax2& loc, const gp_Vec& prism_vec, int pt_size/* = 12*/) const
{
    Font_BRepFont font("consola", Font_FontAspect::Font_FontAspect_Regular, (double)pt_size);
    Graphic3d_HorizontalTextAlignment halign = Graphic3d_HTA_CENTER;
    Graphic3d_VerticalTextAlignment valign = Graphic3d_VTA_CENTER;

    Font_BRepTextBuilder font_brep;
    TopoDS_Shape text_face = font_brep.Perform(font, text, loc, halign, valign);
    TopoDS_Shape text_solid = BRepPrimAPI_MakePrism(text_face, prism_vec);
    return text_solid;
}

TopoDS_Shape FanMaker::MakeCylinder(const gp_Ax2& loc, double radius, double h_positive,
    double h_negative/* = 0*/) const
{
    if (h_negative != 0) // 严格判等
    {
        gp_Ax2 bot_loc = loc.Translated(gp_Vec(loc.Direction()) * h_negative);
        return BRepPrimAPI_MakeCylinder(bot_loc, radius, h_positive - h_negative);
    }
    else
    {
        return BRepPrimAPI_MakeCylinder(loc, radius, h_positive);
    }
}

TopoDS_Face FanMaker::MakeFace(const std::vector<TopoDS_Edge>& edges) const
{
    BRepBuilderAPI_MakeWire mk_wire;
    for (const auto& e : edges)
    {
        mk_wire.Add(e);
    }
    if (!mk_wire.IsDone())
    {
        return TopoDS_Face();
    }
    TopoDS_Wire wire = mk_wire.Wire();
    wire.Closed();
    TopoDS_Face face = BRepBuilderAPI_MakeFace(wire);
    return face;
}

TopoDS_Shape FanMaker::Extrude(const std::vector<TopoDS_Edge>& edges, const gp_Vec& vec) const
{
    TopoDS_Face face = MakeFace(edges);
    if (face.IsNull())
    {
        return TopoDS_Shape();
    }
    TopoDS_Shape extrude_shape = BRepPrimAPI_MakePrism(face, vec);
    return extrude_shape;
}

TopoDS_Shape FanMaker::Revolve(const std::vector<TopoDS_Edge>& edges, const gp_Ax1& axis) const
{
    TopoDS_Face face = MakeFace(edges);
    if (face.IsNull())
    {
        return TopoDS_Shape();
    }
    TopoDS_Shape revol = BRepPrimAPI_MakeRevol(face, axis);
    return revol;
}

TopoDS_Shape FanMaker::CircularPattern(const TopoDS_Shape& shape, const gp_Ax1& axis, int num) const
{
    // 网条阵列
    gp_Trsf trsf;
    trsf.SetRotation(axis, M_PI * 2.0 / double(num));
    std::vector<TopoDS_Shape> pattern(num);
    pattern[0] = shape;
    for (size_t i = 1; i < num; i++)
    {
        pattern[i] = BRepBuilderAPI_Transform(pattern[i - 1], trsf);
    }

    return Compound(pattern);
}

double Length(const TopoDS_Edge& e)
{
    return GCPnts_AbscissaPoint::Length(BRepAdaptor_Curve(e));
}

TopoDS_Shape FanMaker::Fillet(const TopoDS_Shape& shape, double radius, int longest_most/* = 2*/) const
{
    TopTools_IndexedMapOfShape edges;
    TopExp::MapShapes(shape, TopAbs_EDGE, edges);

    std::vector<TopoDS_Edge> all_edges;
    for (auto it = edges.cbegin(); it != edges.cend(); ++it)
    {
        all_edges.emplace_back(TopoDS::Edge(*it));
    }
    std::sort(all_edges.begin(), all_edges.end(), [](const TopoDS_Edge& e1, const TopoDS_Edge& e2)
        {
            return Length(e1) > Length(e2);
        });

    BRepFilletAPI_MakeFillet mk_fillet(shape);
    for (size_t i = 0; i < all_edges.size() && i < longest_most; i++)
    {
        mk_fillet.Add(radius, all_edges[i]);
    }
    mk_fillet.Build();
    if (mk_fillet.IsDone())
    {
        return mk_fillet.Shape();
    }
    return TopoDS_Shape();
}

void FanMaker::OnButtonClicked(size_t button)
{
    if (button >= _buttons.size() || button == _prevButton)
    {
        return;
    }

    Quantity_Color button_color = Color(_d->buttonColorR, _d->buttonColorG, _d->buttonColorB);
    if (-1 != _prevButton)
    {
        _buttons[_prevButton]->SetColor(button_color);

        _buttons[_prevButton]->SetLocalTransformation(gp_Trsf()); // 复位
    }

    gp_Vec press_travel(0, _d->buttonTravel, 0);
    gp_Trsf trsf;
    trsf.SetTranslation(press_travel);
    Quantity_Color pressed_color = Color(164, 102, 0);

    auto curr_button = _buttons[button];
    if (!curr_button)
    {
        return;
    }
    _buttons[button]->SetColor(pressed_color);
    _buttons[button]->SetLocalTransformation(trsf); // 按下

    _prevButton = button;
    _context->UpdateCurrentViewer();

    if (0 == button)
    {
        _buttons[button]->SetColor(button_color);
        _buttons[button]->SetLocalTransformation(gp_Trsf()); // 0, 自动复位
        _context->UpdateCurrentViewer();
    }

    _fanRPM = button * 30;
    OnFanRunning();
}

void FanMaker::OnFanRunning()
{
    if (_fanRPM < 1)
    {
        return;
    }

    const int interval = 35; // 刷新间隔，ms

    gp_Trsf prev_loc = _fan->LocalTransformation();

    gp_Trsf running_trsf;
    running_trsf.SetRotation(FanRevolLocBase().Axis(),
        -_fanRPM / 60.0 / 1000.0 * interval * M_PI * 2);

    gp_Trsf new_loc = running_trsf.Multiplied(prev_loc);
    _fan->SetLocalTransformation(new_loc);
    _context->UpdateCurrentViewer();

    // TODO: 将刷新时间计算进去
    QTimer::singleShot(interval, this, &FanMaker::OnFanRunning);
}

Handle(AIS_InteractiveObject)  FanMaker::DisplayShape(const TopoDS_Shape& shape, const QString& name,
    const Quantity_Color& color) const
{
    if (shape.IsNull())
    {
        return nullptr;
    }
    Handle(AIS_Shape) ais_shape = new AIS_Shape(shape);

    if (color == Quantity_Color()) // 使用随机颜色
    {
        std::srand(time(0));
        const int max = 500;
        const int min = 10;
        int new_color = min + std::rand() % (max - min);
        ais_shape->SetColor(Quantity_Color(Quantity_NameOfColor(new_color)));
    }
    else
    {
        ais_shape->SetColor(color);
    }

    _d->ms->RecordModel(ais_shape, name);
    //_context->Display(ais_shape, false); // 不刷新

    return ais_shape;
}

Handle(AIS_InteractiveObject) FanMaker::DisplayBlades(const std::vector<TopoDS_Shape>& blades) const
{
    std::vector<Quantity_Color> colors{
        Color(255, 0, 0),
        Color(255, 127, 0),
        Color(255, 255, 0),
        Color(0, 255, 0),
        Color(0, 0, 255),
    };

    TopoDS_Shape comp = Compound(blades);
    Handle(AIS_ColoredShape) ais = new AIS_ColoredShape(comp);
    for (size_t i = 0; i < blades.size(); i++)
    {
        ais->SetCustomColor(blades[i], colors[i % colors.size()]);
    }

    _d->ms->RecordModel(ais, "阵列叶片");

    return ais;
}

void FanMaker::DisplayPnts(const std::vector<gp_Pnt>& pnts, const QString& name) const
{
    std::vector<TopoDS_Shape> shapes(pnts.size());
    for (size_t i = 0; i < pnts.size(); i++)
    {
        shapes[i] = BRepBuilderAPI_MakeVertex(pnts[i]);
    }
    DisplayShape(Compound(shapes), name, Quantity_NOC_RED);
    return;

    Handle(AIS_PointCloud) ais = new AIS_PointCloud();
    Handle(TColgp_HArray1OfPnt) hpnts = new TColgp_HArray1OfPnt(1, (int)pnts.size());
    for (size_t i = 0; i < pnts.size(); i++)
    {
        hpnts->SetValue(int(i + 1), pnts[i]);
    }
    Handle(Prs3d_PointAspect) aspect = new Prs3d_PointAspect(Aspect_TOM_O_PLUS, Quantity_NOC_RED, 10);
    ais->Attributes()->SetPointAspect(aspect);
    ais->SetPoints(hpnts);
    _context->Display(ais, false); // 不刷新
}
